package lol.clann.bukkit.pluginboot.config.jackson.nbt;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lol.clann.bukkit.pluginboot.util.NBTCompoundUtils;
import lol.clann.bukkit.pluginboot.util.NBTListUtils;
import lol.clann.bukkit.pluginboot.util.NBTUtils;
import lol.clann.clannboot.annotation.inject.Inject;
import lol.clann.clannboot.annotation.svc.PostInstance;
import lol.clann.clannboot.annotation.svc.Service;
import lol.clann.clannboot.config.jackson.Jackson;
import lol.clann.clannboot.config.jackson.fill.BaseJsonDeserializer;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @author pyz
 * @date 2018/10/12 上午12:22
 */
@Service(priority = Integer.MAX_VALUE - 100)
public class NBTDeserializerRegister {

    @Inject
    private NBTUtils nbtUtils;
    @Inject
    private NBTCompoundUtils nbtCompoundUtils;
    @Inject
    private NBTListUtils nbtListUtils;
    @Inject
    private ObjectMapper objectMapper;

    @PostInstance
    private void init() {
        // NBTTagCompound
        Jackson.addDeserializer(NBTUtils.classNBTTagCompound.getHandle(), new BaseJsonDeserializer<Object>() {
            @Override
            protected Object create(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
                Object tag = nbtUtils.newNBTCompound();
                ObjectNode tree = objectMapper.readTree(p);
                Iterator<Map.Entry<String, JsonNode>> it = tree.fields();
                while (it.hasNext()) {
                    Map.Entry<String, JsonNode> next = it.next();
                    if (next.getValue().isObject()) {
                        nbtCompoundUtils.setTag(tag, next.getKey(), objectMapper.readValue(next.getValue().traverse(), NBTUtils.classNBTTagCompound.getHandle()));
                    } else if (next.getValue().isArray()) {
                        nbtCompoundUtils.setTag(tag, next.getKey(), objectMapper.readValue(next.getValue().traverse(), NBTUtils.classNBTTagList.getHandle()));
                    } else {
                        String content = next.getValue().asText();
                        NBTConventorNode conventor = NBTUtils.getByKey(content.substring(0, 2));
                        nbtCompoundUtils.setTag(tag, next.getKey(), conventor.getDeserializer().apply(content.substring(2, content.length())));
                    }
                }
                return tag;
            }

            @Override
            protected Object fill(Object value, JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
                Object tag = create(p, context);
                Set<String> keys = nbtCompoundUtils.getKeySet(tag);
                for (String key : keys) {
                    Object node = nbtCompoundUtils.getTag(tag, key);
                    nbtCompoundUtils.setTag(value, key, node);
                }
                return value;
            }
        });
        // NBTTagList
        Jackson.addDeserializer(NBTUtils.classNBTTagList.getHandle(), new JsonDeserializer<Object>() {
            @Override
            public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
                Object tagList = nbtUtils.newNBTList();
                ArrayNode arr = objectMapper.readTree(p);
                for (JsonNode jsonNode : arr) {
                    if (jsonNode.isObject()) {
                        nbtListUtils.appendTag(tagList, objectMapper.readValue(jsonNode.traverse(), NBTUtils.classNBTTagCompound.getHandle()));
                    } else if (jsonNode.isArray()) {
                        nbtListUtils.appendTag(tagList, objectMapper.readValue(jsonNode.traverse(), NBTUtils.classNBTTagList.getHandle()));
                    } else {
                        String content = jsonNode.asText();
                        NBTConventorNode conventor = NBTUtils.getByKey(content.substring(0, 2));
                        nbtListUtils.appendTag(tagList, conventor.getDeserializer().apply(content.substring(2, content.length())));
                    }
                }
                return tagList;
            }
        });
    }

}
